/* -+- file  ./kf2/Core/ConnectionManager.cs -+- */
/*
 * kf2 jabber client
 * ~~~~~~~~~~~~~~~~~
 *
 * Copyright (C) 2006-2007 Przemysław Sitek
 * 				<przemyslaw.sitek at wp.pl> 
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this program; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

using System;
using System.Collections;
using System.Collections.Generic;
using System.Xml;
using Habazie.kf2.Core;
using Habazie.kf2.Net;
using Habazie.kf2.Events;

namespace Habazie.kf2.Core
{
	public delegate void AccountEventHandler (object o, AccountEventArgs args);

	///<summary>Holds a collection of Accounts</summary>
	public class ConnectionManager : IEnumerable
	{
		#region Fields
		
		///<summary>List of Accounts</summary>
		private LinkedList<Account> m_Accounts;
		
		///<summary>Event dispatcher</summary>
		private EventBox m_EventBox;
		
		private IQManager m_iqmanager;
		
		/// <summary>
		/// 	Something has somehow changed
		/// </summary>
		public event EventHandler Changed = null;
		
		/// <summary>
		/// 	Account was added
		/// </summary>
		public event AccountEventHandler AccountAdded = null;
		
		/// <summary>
		/// 	Account was removed 
		/// </summary>
		public event AccountEventHandler AccountRemoved = null;

		#endregion
		#region Constructors
		
		public ConnectionManager (App a)
		{
			m_Accounts = new LinkedList<Account> ();
			m_EventBox = new EventBox (a);
			m_iqmanager = new IQManager ();
			
			m_iqmanager.AddHandler ("jabber:iq:version", new jabber.client.IQHandler (VersionHandler));
		}
		
		#endregion
		#region Properties
		
		public ICollection<Account> Accounts {
			get { return m_Accounts; }
		}

		public int Count {
			get { return m_Accounts.Count; }
		}
		
		public Account First {
			get {
				if (m_Accounts.Count > 0)
					return m_Accounts.First.Value;
				else
					return null;
			}
	
		}
			
		public EventBox GetEventBox ()
		{
			return m_EventBox;
		}
		
		#endregion
		
		///<summary>Load accounts information from file</summary>
		public void Load (string file)
		{
			Util.Debug ("Core", "Loading " + file);
			XmlDocument doc = new XmlDocument ();
			try {
				doc.Load (file);
				XmlNode root = doc.DocumentElement;
				foreach (XmlNode node in root.ChildNodes) {
					Util.Debug ("Core", "Processing " + node.Name);
					if (node.Name == "account") {
						Account acc = new Account (node);
						Add (acc, false);
						//acc.OnPresence += PresenceReceived;
						//acc.OnMessage += MessageReceived;
						//m_Accounts.AddLast (acc);
					}
				}
			} catch (Exception e) {
				Util.Debug ("Core", "Exception" + e.Message);
			} finally {
			}
		}
		
		///<summary>Store accounts information to file</summary>
		public void Store (string file)
		{
			XmlTextWriter xml = new XmlTextWriter (file, null);
			xml.Formatting = System.Xml.Formatting.Indented;
			xml.WriteStartElement ("acc");
			foreach (Account a in m_Accounts) {
				a.Store (xml);
			}
			xml.WriteEndElement ();
			xml.Close ();
		}
		
		
		///<summary>Add an account to Account manager</summary> 
		public void Add (Account acc)
		{
			Add (acc, true);
			
			if (AccountAdded != null)
				AccountAdded (this, new AccountEventArgs (acc));
			if (Changed != null)
				Changed (this, new EventArgs ());
		}
		
		protected void Add (Account acc, bool x)
		{
			acc.OnPresence += PresenceReceived;
			acc.OnMessage += MessageReceived;
			acc.OnIQ += IQReceived;
//			acc.AddNamespace ("version", "jabber:iq:version");
//			acc.AddCallback("self::iq[@type='get']/version:query", new jabber.protocol.ProtocolHandler(VersionHandler));
			m_Accounts.AddLast (acc);
		}
		
		///<summary>Remove this account from ConnectionManager</summary>
		public void Remove (Account acc)
		{
			acc.OnPresence -= PresenceReceived;
			acc.OnMessage -= MessageReceived;
			m_Accounts.Remove (acc);
			
			if (AccountRemoved != null)
				AccountRemoved (this, new AccountEventArgs (acc));
			if (Changed != null)
				Changed (this, new EventArgs ());
		}


		/// <summary>
		/// 	Fire Changed signal
		/// </summary>
		public void FireChanged ()
		{
			if (Changed != null)
				Changed (this, new EventArgs ());
		}
		

		///<summary>Set presence information</summary>
		public void Presence (jabber.protocol.client.PresenceType t, string status, string show)
		{
			/* Check for number of active connections */
			int n_connected = 0;
			foreach (Account acc in m_Accounts) {
				if (acc.IsAuthenticated)
					n_connected++;
			}
			
			if (n_connected == 0) {
				foreach (Account acc in m_Accounts) {
					acc.Connect ();
				}
			} else {
				foreach (Account acc in m_Accounts) {
					if (acc.IsAuthenticated) 
						acc.SetStatus (t, status, show);
				}
			}
		}
		
		// For implementing IEnumerable
		// iterate over accounts
		public IEnumerator GetEnumerator ()
		{
			return m_Accounts.GetEnumerator ();
		}
		
		#region Event handling stuff
		
		protected virtual void PresenceReceived (object sender, jabber.protocol.client.Presence presence)
		{
			Gtk.Application.Invoke (delegate {
				if (presence.Type == jabber.protocol.client.PresenceType.subscribe) {
					m_EventBox.Dispatch ((Account) sender, presence);
				}
			});
		}
		
		protected virtual void MessageReceived (object sender, jabber.protocol.client.Message message)
		{
			Gtk.Application.Invoke (delegate {
				m_EventBox.Dispatch ((Account) sender, message);
			});
		}
		
		protected virtual void IQReceived (object sender, jabber.protocol.client.IQ iq)
		{
			Gtk.Application.Invoke (delegate {
				m_iqmanager.Dispatch (sender as Account, iq);
			});
		}
		
		
		///<summary>Report software version</summary>
		protected virtual void VersionHandler (object sender, System.Xml.XmlElement rp)
		{
			Account acc = (Account) sender;
			if (rp.GetAttribute ("type") != "get")
				return;
			
			Console.WriteLine ("got jabber:iq:version");
			jabber.protocol.iq.VersionIQ iq = new jabber.protocol.iq.VersionIQ (((Account) sender).Document);
			iq.Type =  jabber.protocol.client.IQType.result;
			iq.To = rp.GetAttribute ("from");
			iq.ID = rp.GetAttribute("id");
			jabber.protocol.iq.Version ver = (jabber.protocol.iq.Version) iq.Query;
			ver.EntityName = Util.APPNAME;
			ver.Ver = Util.VERSION;
			if (! Util.Config.GetBool ("hide-os-info"))
				ver.OS = Util.OperatingSystem;
			acc.Write (iq);
		}
		
		
		#endregion
	}

	public class AccountEventArgs : EventArgs
	{
		private Account m_account;
		public AccountEventArgs (Account a) : base ()
		{
			m_account = a;
		}
		public Habazie.kf2.Net.Account Account {
			get { return m_account; }
		}
	}
}
